﻿using System;

namespace Nextensions.Numbers
{
    public static class Calculations
    {
        public static float Progress(int Index, int Total)
        {
            if (Total <= 0)
                return 0;

            float prg = 100 * (float)Index / (float)Total;
            return prg;
        }

        public static double Mean(double[] Set)
        {
            double mean = 0;

            for (int i = 0; i < Set.Length; i++)
            {
                mean += Set[i];
            }

            mean /= Set.Length;

            return mean;
        }

        public static double StandardDeviation(double[] Set, int LowerBound, int UpperBound, double Mean)
        {
            double result = 0;

            for (int i = LowerBound; i <= UpperBound; i++)
            {
                double current = Set[i - 1];

                result += Math.Pow(current - Mean, 2);
            }

            result /= (UpperBound - LowerBound);
            result = Math.Sqrt(result);

            return result;
        }

        public static double Variance(double[] Set, int LowerBound, int UpperBound, double Mean)
        {
            double result = 0;

            for (int i = LowerBound; i <= UpperBound; i++)
            {
                double current = Set[i - 1];

                result += Math.Pow(current - Mean, 2);
            }

            result /= (UpperBound - LowerBound);

            return result;
        }

        /// <summary>
        /// Returns the covariance value between two data sets (dimensions).
        /// </summary>
        /// <param name="SetX"></param>
        /// <param name="SetY"></param>
        /// <param name="LowerBound">1 based lower (start) index.</param>
        /// <param name="UpperBound">1 based upper (end) index</param>
        /// <param name="MeanX"></param>
        /// <param name="MeanY"></param>
        /// <returns></returns>
        public static double Covariance(double[] SetX, double[] SetY, int LowerBound, int UpperBound, double MeanX, double MeanY)
        {
            double result = 0;

            for (int i = LowerBound; i <= UpperBound; i++)
            {
                double currentX = Math.Round(SetX[i - 1], 2);
                double currentY = Math.Round(SetY[i - 1], 2);

                double currentDiff = Math.Round(Math.Round(currentX - MeanX, 2) * Math.Round(currentY - MeanY, 2), 2);

                result += currentDiff;
            }

            result /= (UpperBound - LowerBound);

            return result;
        }

        public static double[] MatrixColumnVectorProduct(double[,] Matrix, double[] Vector)
        {
            if (Matrix.GetLength(0) != Vector.Length)
                throw new ArgumentOutOfRangeException("The vector length must be the same as the number of columns in the matrix.");

            int vectorLen = Vector.Length;
            int rowLen = Matrix.GetLength(1);
            double rowVal;
            double[] vectorProduct = new double[Vector.Length];

            for (int row = 0; row < rowLen; row++)
            {
                rowVal = 0;

                for (int col = 0; col < vectorLen; col++)
                {
                    rowVal += Matrix[row, col] * Vector[col];
                }

                vectorProduct[row] = rowVal;
            }

            return vectorProduct;
        }

        public static double[,] CovarianceMatrix(Vector<double>[] Dimensions)
        {
            int rows = Dimensions.Length;
            int cols = rows; 
            
            double[,] covarMatrix = new double[rows, cols]; // The matrix must be square
            
            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    Vector<double> dimI = Dimensions[row];
                    Vector<double> dimJ = Dimensions[col];
                    covarMatrix[row, col] = Covariance(dimI.Data, dimJ.Data, dimI.LowerBound, dimI.UpperBound, dimI.Mean, dimJ.Mean);
                }
            }

            return covarMatrix;
        }
    }
}
